#ifndef __CLIENT_H__
#define __CLIENT_H__

#include <stdlib.h>
// File handling
#include <stdio.h>
// for int conversion
#include <sstream>
#include <string.h>
#include <vector>
#include <list>
#include <iostream>

#include <unistd.h>

#ifdef __WIN32__

// pdcurses calls it KEY_BACKSPACE
#define KEY_DELETE 8

#else
#define KEY_DELETE 127

#endif

#include <signal.h>
#include <curses.h>
#include <errno.h>
#include <math.h>

#include "Dispatch.h"

// Might move to Dispatch.h
#include <iostream>
#include <fstream>

typedef struct {
	uint16_t uid;
	std::string username;
	bool mark_for_deletion;
	struct sockaddr_in file_connection;
} Participant;

typedef enum {
	// File transfer has completed. (Flag for removal from queue)
	COMPLETE,
	
	// File transfer has been canceled. (Flag for removal)
	CANCELED,
	
	// File transfer has been requested, not yet accepted.
	PENDING,
	
	// File transfer has been accepted (and is assumed to be running).
	ACCEPTED,
	
	// File trasnfer has been paused.
	PAUSED
} FileTransferStatus;

typedef struct {
	
	// From or To client
	Participant *client;
	std::string filename;
	uintptr_t uid;
	
	size_t filesize;
	size_t bytes_transfered;
	
	FILE *file;

	// Whether the file transfer is in progress
	FileTransferStatus status;
	
} FileTransfer;

bool marked_for_deletion (const Participant* p);
bool ft_marked_for_deletion (const FileTransfer* f);

class Client : public Participant {
	public:
		Client();
		void run(void);
		bool isRunning;
		virtual ~Client();
		
		class Command
		{
			public:
				Command(std::string c, std::string d, std::string u) : cmd(c), dsc(d), use(u){};
				std::string cmd;
				std::string dsc;
				std::string use;
		};
		
	protected:
		
		typedef struct {
			uint16_t speaker;
			std::string message;
		} Message;
	
		struct sockaddr_in chat_connection;
	
		bool new_connection;
		
		// Max file descriptor for multiplexing
		int max_fd;
		
		// Socket set for switching between STDIN and the server connection
		fd_set switcher;
		
		// Host name
		struct hostent *host;
		unsigned int cursor;
		unsigned int scrollPos;
		unsigned int characters;
		char message[DATA_SIZE];
		
		std::vector<Message*> conversation;
		std::list<Participant*> participants;
	
		// Pending transfers going out
		std::list<FileTransfer*> o_transfers;
		// Pending transgers coming in
		std::list<FileTransfer*> i_transfers;
	
		/* Returns true if there are any active transfers
		 * in the passed 'transferList'
		 **************************************************/
		bool anyActiveFileTransfers( std::list<FileTransfer*>& transferList );
	
		/* Returns the next unique file transfer ID (UFTID)
		 **************************************************/
		uint16_t nextUFTID( std::list<FileTransfer*>& transferList );
		void handleIncomingFileTransfers( void );
		void handleOutgoingFileTransfers( void );
		void handleIncomingFileAccept( std::string args );
		void handleIncomingFileReject( std::string args );
		
		// Operating Specifics
		virtual void catchInput(void) = 0;
		virtual void addLine(int, std::string) = 0;
		void _addLine(int, const char *message, ...);
		virtual void updateToolbar(void) = 0;
		virtual void updateWindow(void) = 0;
		virtual void drawConversation(void) = 0;
		
		// Standard Client Functions
		void clearTextField(void);
		void processCommands(char*);
		void processDispatch(Dispatch* d);
		bool establishTCPConnection( const std::string ip );
		void establishUDPConnection( const std::string ip );
		void disconnect(void);
		bool validIP( const std::string ip );
		void updateInfo( std::string data );
	
		void handleIncomingClientMessage( Dispatch *d );
		void handleOutgoingClientMessage( void );
	
		void requestUserList( void );
		void updateUserList( std::string data, bool silent = FALSE );
		void listUsers( void );
		void setUsername( std::string u );
	
		/* Client (A) has recieved acceptance from another
		 * client (B) to transfer a file from (A to B).
		 **************************************************/
		void handleOutgoingFileAccept( Dispatch *d );
	
		/* Client (A) has recieved a request from another
		 * client (B) to transfer a file (from B to A).
		 **************************************************/
		void handleIncomingFileRequest( Dispatch *d );
	
		/* Client (A) is requesting to send a file to
		 * client (B). (Creates dispatch and sends)
		 **************************************************/
		void handleOutgoingFileRequest( const char* args );
	
		void readLine( char*, FILE*);
	
		// Attempt a punchthrough udp connection
		void punch( void );
		
		// Runtime Functions
		void nick( const char* args );
		void help( const char* args );
		void info( const char* args );
		void quit( const char* args );
	
		Participant *findParticipantByUID( unsigned int p_id );
		Participant *findParticipantByUsername( std::string p_name );
	
	private:
	
		int chat_socket;
		int file_socket;
};

#endif
